<?php

class AbstractObject
{
    protected $__readonly_fields = array();

    function __construct()
    {
    	$args =& func_get_args();
    	if (!empty($args[0])) {
	    	if (is_object($args[0]) && is_a($args[0], __CLASS__)) {
	    		$this->__copy($args[0]);
	    		return;
	    	}
    	}
    }

    function __destruct() {}

    public
    function __unset($property)
    {
        try {
            $this->{$property} = null;
        } catch (Exception $e) {};
    }

    public
    function __isset($property)
    {
    	$status = null;
    	try {
    	   $status =& $this->{$property};
    	} catch (Exception $e) {
    	    return false;
    	}
    	return !is_null($status);
    }

    public 
    function __set($property, $value)
    {
        if ($property == '_readonly_fields') {
            throw new Exception($property);
        }
        if (in_array($property, $this->__readonly_fields)) {
            throw new Exception("Field «{$property}» is readonly.");
        }
        if (method_exists($this, '__set_'.$property)) {
            $method = '__set_'.$property;
            @$this->{$method}($value);
            if (method_exists($this, '__get_'.$property)) {
                $method = '__get_'.$property;
                return $this->{$method}();
            }
        } elseif (property_exists($this, '_'.$property)) {
        	$_property = '_'.$property;
            $this->{$_property} =& $value;
        } else {
            throw new Exception($property);
        }
    }

    public 
    function &__get($property)
    {
        if ($property == '_readonly_fields') {
            throw new Exception($property);
        }
        if (method_exists($this, '__get_'.$property)) {
            $method = '__get_'.$property;
            $tmp =& $this->{$method}();
            return $tmp;
        } elseif (property_exists($this, '_'.$property)) {
        	$_property = '_'.$property;
            return $this->{$_property};
        } else {
            throw new Exception($property);
        }
    }
    
    /*
    public $var1;
    public $var2;

    public static function __set_state($an_array) // As of PHP 5.1.0
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
	*/
    
    protected function __copy($obj)
    {
    	if (!(is_object($obj) && is_a($obj, __CLASS__))) {
    		throw new ArgException();
    	}
	    $this->__readonly_fields = $obj->__readonly_fields;
    }
}

function is_reference(&$obj, $var_name)
{
    return (bool)mb_ereg("[ ]{2}\\[\"{$var_name}\"\\]=>\n[ ]{2}&\\w+", svar_dump($obj));
}

/*
ob_start();
debug_zval_dump($a);
$zval = ob_get_clean();
$is_reference = array_key_exists('a', get_defined_vars()) && preg_match('/ refcount\(1\)/', $zval);
*/

function svar_dump()
{
    $args =& func_get_args();
    ob_start();
    call_user_func_array('var_dump', $args);
    return ob_get_clean();
}

function are_identical(&$a, &$b)
{
    if ($a !== $b) {
        return false;
    }
    $c = $a;
    if (is_null($a)) {
        $a = 1;
        if ($b === 1) {
            $a = $c;
            return true;
        }
    } else {
        $a = null;
        if (is_null($b)) {
            $a = $c;
            return true;
        }
    }

    $a = $c;
    return false;
}

function &____createO($class, $args=array())
{
    $classReflection = new ReflectionClass($class);
    if (count($args)) {
        $instance =& $classReflection->newInstanceArgs($args);
    } else {
        $instance =& $classReflection->newInstance();
    }
    return $instance;
}

abstract class Singleton
{
    private static $instance;

    private function __construct() {}

    private function __clone() {}

    public static function &o()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self;
        }
        return self::$instance;
    }
}

?>